# backend/app/scan.py
import os
import re
from tinytag import OtherFields, TinyTag
from pypinyin import pinyin, Style
from app.database import Audio, SessionLocal, bulk_insert_audios, dart_string_hash_code
from app.supply import DATA_PATH, MUSIC_PATH, log


# 支持的音频格式列表
audio_types = [
    "flac",
    "mpc",
    "speex",
    "wavpack",
    "true-audio",
    "lpac",
    "wav",
    "aiff",
    "ogg",
    "oga",
    "opus",
    "m4a",
    "ac3",
    "m4r",
    "m4b",
    "mp3",
    "mp4",
    "ape",
    "asf",
    "dsf",
    "dff",
    "aac",
    "wma",
    "adp",
    "adx",
    "amr",
    "amrnb",
    "amrwb",
    "au",
    "caf",
    "dts",
    "eac3",
    "gsm",
    "qcp",
    "ra",
    "rm",
    "voc",
]


# 初始化文件夹
def initialize_folders():
    log("Initializing folders...")
    folders = [
        DATA_PATH / "covers",
        DATA_PATH / "lyrics",
        DATA_PATH / "downloads",
        DATA_PATH / "medias",
        DATA_PATH / "database",
    ]

    for folder in folders:
        if not folder.exists():
            folder.mkdir(parents=True, exist_ok=True)
            log(f"Created folder: {folder}")


def scan_audio_files(path):
    """
    扫描指定路径下的所有音频文件
    :param path: 文件夹路径
    :return: 音频文件路径列表
    """
    audio_files = []
    for root, dirs, files in os.walk(path):
        for file in files:
            if any(file.lower().endswith(f".{ext}") for ext in audio_types):
                audio_files.append(os.path.join(root, file))
    return audio_files


def get_audio_metadata(file_path):
    """
    使用 tinytag 获取音频文件的元数据
    :param file_path: 音频文件路径
    :return: 元数据字典
    """

    hash_value = dart_string_hash_code(file_path)
    try:
        tag = TinyTag.get(file_path, image=True)
        image = save_cover(tag, file_path)
        save_lyric(tag, file_path)
        title = tag.title or tag.filename
        letter = get_letter(title)
        metadata = {
            "favorite": False,
            "year": tag.year,  # 需要时填入 datetime 对象
            "language": None,
            "album": tag.album or "[Unknown Album]",
            "artist": tag.artist or "[Unknown Artist]",
            "album_id": tag.album or "[Unknown Album]",
            "artist_id": tag.artist or "[Unknown Artist]",
            "title": title,
            "letter": letter,
            "track_number": tag.track,  # Drift 原字段: trackNumber
            "lyric": None,
            "bitrate": int(tag.bitrate) if tag.bitrate is not None else None,
            "samplerate": tag.samplerate,
            "suffix": None,
            "url": file_path,
            "size": tag.filesize,
            "file": "",
            "cover": image,
            "genre": tag.genre,
            "duration": int(tag.duration) if tag.duration is not None else None,
            "last_played": None,  # 需要时填入 datetime 对象
            "category": 0,  # 关联 Category.id
            "extra": None,
            "source_type": 0,  # 必填字段（根据业务设置有效值）
            "hash": hash_value,
        }
        return metadata
    except Exception as e:
        log(f"Error reading metadata from {file_path}: {e}")
        title = os.path.basename(file_path)
        letter = get_letter(title)
        return {
            "favorite": False,
            "year": None,  # 需要时填入 datetime 对象
            "language": None,
            "album": "[Unknown Album]",
            "artist": "[Unknown Artist]",
            "album_id": "[Unknown Album]",
            "artist_id": "[Unknown Artist]",
            "title": title,
            "letter": letter,
            "track_number": None,  # Drift 原字段: trackNumber
            "lyric": None,
            "bitrate": None,
            "samplerate": None,
            "suffix": None,
            "url": file_path,
            "size": None,
            "file": "",
            "cover": None,
            "genre": None,
            "duration": None,
            "last_played": None,  # 需要时填入 datetime 对象
            "category": 0,  # 关联 Category.id
            "extra": None,
            "source_type": 0,  # 必填字段（根据业务设置有效值）
            "hash": hash_value,
        }


def scan_and_extract_metadata():
    """
    扫描路径并提取所有音频文件的元数据
    :return: 元数据列表
    """
    audio_files = scan_audio_files(MUSIC_PATH)
    metadata_list = []
    for file_path in audio_files:
        metadata = get_audio_metadata(file_path)
        if metadata:
            metadata_list.append(metadata)
    with SessionLocal() as session:
        bulk_insert_audios(metadata_list=metadata_list, session=session)
    return metadata_list


def save_cover(tag: TinyTag, file_path: str):
    """
    保存封面图片到本地
    :param tag: 音频文件元数据
    :param file_path: 音频文件路径
    :return: 保存成功的图片路径
    """
    cover_extensions = [".jpg", ".JPG", ".png", ".PNG"]
    for ext in cover_extensions:
        cover_path = f"{ os.path.splitext(file_path)[0]}{ext}"
        if os.path.exists(cover_path):
            return cover_path

    if tag is None or not tag.images.any:
        return ""

    cover_image = tag.images.any
    hash_value = dart_string_hash_code(file_path)
    cover_path = DATA_PATH / "covers" / f"{hash_value}.jpg"

    if cover_path.exists():
        return str(cover_path)

    with open(cover_path, "wb") as f:
        f.write(cover_image.data)

    return str(cover_path)


def save_lyric(tag: TinyTag, file_path: str):
    """
    保存歌词到本地
    :param file_path: 音频文件路径
    :return: 保存成功的歌词路径
    """
    lyric_extensions = [".qrc", ".QRC", ".krc", ".KRC", ".lrc", ".LRC"]
    for ext in lyric_extensions:
        lyric_path = f"{ os.path.splitext(file_path)[0]}{ext}"
        if os.path.exists(lyric_path):
            return lyric_path

    other_fields: OtherFields = tag.other
    lyrics: list[str] | None = other_fields.get("lyrics")

    if len(lyrics) > 0:
        hash_value = dart_string_hash_code(file_path)
        lrc_path = DATA_PATH / "lyrics" / f"{hash_value}.lrc"

        with open(lrc_path, "w", encoding="utf-8") as f:
            if len(lyrics) > 1:
                f.write(f"{lyrics[0]}┃{lyrics[1]}")
            else:
                f.write(lyrics[0])
        return str(lrc_path)
    return None


def get_letter(text: str, style=Style.FIRST_LETTER) -> str:
    if not text or len(text.strip()) == 0:
        return ""

    first_char = text[0]

    # 拉丁字母判断（A-Z, a-z）
    if re.match(r"^[A-Za-z]$", first_char):
        return first_char.upper()

    # 中文字符判断（兼容扩展汉字区）
    if re.match(r"[\u4e00-\u9fff\u3400-\u4dbf\U00020000-\U0002a6df]", first_char):
        pinyin_list = pinyin(first_char, style=Style.FIRST_LETTER, heteronym=False)
        if pinyin_list and len(pinyin_list[0]) > 0:
            return pinyin_list[0][0].upper()

    # 其他字符返回空
    return ""
